
#include <QApplication>
#include <QDebug>
#include <QFile>
#include <QSplitter>
#include <QTextBrowser>
#include <QHeaderView>
#include <QHBoxLayout>
#include <QMenu>
#include <QClipboard>

#include "filetree.h"
#include "hexwidget.h"
#include "filestream.h"


FileTree::FileTree(QWidget *parent) :
    QWidget(parent)
{
    initWidget();
    m_findBytes.clear();
}

FileTree::~FileTree()
{
}

/* Public Methods */

bool FileTree::setFile(const QString &fileName, const QString &dllPath)
{
    closeFile();

    QFile file(fileName);
    file.open(QIODevice::ReadOnly);
    QByteArray bytes = file.read(file.size());
    file.close();

    m_hex->setData(bytes);
    return parseData(bytes, dllPath);
}

void FileTree::closeFile()
{
    m_hex->setData(QByteArray());
    m_tree->clear();
    m_findBytes.clear();
}

void FileTree::setFindBytes(QByteArray data)
{
    m_findBytes.clear();
    m_findBytes = data;
}

/* Public & Private Slots */

void FileTree::copy()
{
    QClipboard *clipboard = QApplication::clipboard();
    QByteArray data = m_hex->getSelectData();
    clipboard->setText(data.toHex(' '));
}

void FileTree::findNext()
{
    qint64 pos = m_hex->findBytes(m_findBytes, true);
    if (pos > 0) {
        onHexSelect(pos);
    }
}

void FileTree::findLast()
{
    qint64 pos = m_hex->findBytes(m_findBytes, false);
    if (pos > 0) {
        onHexSelect(pos);
    }
}

void FileTree::onHexSelect(qint64 pos)
{
    QTreeWidgetItem *item = getHexSelectItem(QTreeWidgetItemIterator(m_tree), pos);
    if (item == nullptr) return;

    while (item->childCount() > 0) {
        QTreeWidgetItem *child = getHexSelectItem(QTreeWidgetItemIterator(item->child(0)), pos);
        if (child == nullptr) break;
        item = child;
    }

    m_tree->setCurrentItem(item);
    onTreeSelect(item, 0);
}

void FileTree::onTreeSelect(QTreeWidgetItem *item, int)
{
    TreeItem *i = (TreeItem*) item;
    m_hex->setSelect(i->dataPos, i->dataLen);
    emit selectChanged(i->dataPos, i->dataLen);
}

void FileTree::onPauseError(int line)
{
    QString log = QString("Pause file error, line=%1").arg(line);
    onPauseLog(LOG_Error, log);
    emit onMessage(log);
}

void FileTree::onPauseLog(int type, QString log)
{
    QString typeStr;
    switch(type) {
    case LOG_Verbose: typeStr = "Verbose"; break;
    case LOG_Debug:   typeStr = "Debug";   break;
    case LOG_Info:    typeStr = "Info";    break;
    case LOG_Warning: typeStr = "Warning"; break;
    case LOG_Error:   typeStr = "Error";   break;
    }
    log = QString("%1: %2").arg(typeStr, log);
    m_log->append(log);
}

/* Private Methods */

void FileTree::initWidget()
{
    QSplitter *hor = new QSplitter(Qt::Horizontal, this);
    hor->setChildrenCollapsible(false);
    QHBoxLayout *layout = new QHBoxLayout(this);
    layout->addWidget(hor);

    m_tree = new QTreeWidget(this);
    hor->addWidget(m_tree);
    m_tree->setHeaderHidden(true);
    m_tree->header()->setSectionResizeMode(QHeaderView::ResizeToContents); // 动态列宽
    m_tree->header()->setStretchLastSection(false); // 树节点内容显示不省略
    // m_tree->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); // 添加水平滚动条
    // m_tree->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); // 水平自动滚动
    m_tree->setIndentation(12); // 行缩进
    connect(m_tree, SIGNAL(itemClicked(QTreeWidgetItem*, int)),
            this, SLOT(onTreeSelect(QTreeWidgetItem*, int)));

    QSplitter *ver = new QSplitter(Qt::Vertical, this);
    ver->setChildrenCollapsible(false);
    hor->addWidget(ver);

    m_hex = new HexWidget(this);
    ver->addWidget(m_hex);
    connect(m_hex, SIGNAL(onSelect(qint64)), this, SLOT(onHexSelect(qint64)));

    m_log = new QTextBrowser(this);
    ver->addWidget(m_log);
    m_log->document()->setMaximumBlockCount(100); // 最多100行
}

bool FileTree::parseData(QByteArray &data, const QString &dllPath)
{
    TreeItem *root = new TreeItem;
    root->setText(0, "root");
    root->dataPos = 0;
    m_tree->addTopLevelItem(root);

    FileStream stm("");
    connect(&stm, SIGNAL(onError(int)), this, SLOT(onPauseError(int)));
    connect(&stm, SIGNAL(onLog(int, QString)), this, SLOT(onPauseLog(int, QString)));
    bool ret = stm.loadDllLibrary(dllPath);
    if (!ret) {
        QString log = "load dll file err: " + dllPath;
        onPauseLog(LOG_Error, log);
        emit onMessage(log);
        return false;
    }
    stm.setData(data);
    ret = stm.runPauseDll(root);
    stm.closeLibrary();

    m_tree->update();
    return ret;
}

QTreeWidgetItem *FileTree::getHexSelectItem(QTreeWidgetItemIterator it, qint64 pos)
{
    while (*it) {
        TreeItem *item = (TreeItem*) *it;
        if (item->dataPos <= pos && item->dataPos + item->dataLen > pos) {
            return *it;
        }
        ++it;
    }
    return nullptr;
}
